home
***
CD-ROM
|
disk
|
FTP
|
other
***
search
/
PC Media 20
/
PC MEDIA CD20.iso
/
share
/
prog
/
cursoasm
/
cap8.msg
< prev
next >
Wrap
Text File
|
1993-07-16
|
12KB
|
227 lines
INTRODUCCION AL ASM: USO DE LOS PROGRAMAS ENSAMBLADORES (II)
============================================================
Continuamos en este octavo capítulo del curso la explicación del HELLO.ASM
en el punto donde la dejamos al final del anterior capítulo.
El último segmento del programa es el segmento de código, al que le hemos
dado el nombre 'CODIGO'. La primera línea de este segmento es un ASSUME, cuya
función vamos a ver detalladamente.
Cuando vimos los distintos modos de direccionamiento, vimos que cada uno
usaba un registro segmento por defecto (por cierto, uno de los registros de
segmento estaba equivocado: el modo [BP+xx] usa el segmento SS por defecto, y
no el DS como aparecía). Cuando queremos que una instrucción que opera por
defecto con un segmento opere con otro, es necesario anteponer al operando que
referencia a memoria el nombre del segmento seguido de dos puntos. Por ejemplo,
veamos la siguiente instrucción, que accede al segmento apuntado por DS:
mov ax,[0]
Si queremos que cargue la primera palabra del segmento apuntado por CS:
mov ax,cs:[0]
A nivel de código de máquina, lo que se hace es prefijar la instrucción an-
terior con un byte (existen cuatro posibles valores, uno para cada segmento)
que especifica que no se debe usar el segmento por defecto. A este prefijo se
le suele denominar 'segment override'.
Cuando en un listado en lenguaje ensamblador se genera un acceso a memoria,
el ensamblador es capaz de meter automáticamente 'segment override's donde sea
necesario (además de permitirnos especificarlos a nosotros). Para ello, hay que
decirle al ensamblador a cual de los segmentos que hemos definido en el listado
está apuntando cada uno de los registros de segmento. Así, cuando generamos una
referencia a una etiqueta, comprueba en qué segmento ha sido definida y busca
cuál de los registros de segmento apunta a ése segmento. Así, puede decidir si
es necesario generar un 'segment override', y cuál de ellos es necesario. la
directiva 'ASSUME' dice al ensamblador a qué segmento apunta cada registro de
segmento, como vemos en HELLO.ASM. De todas maneras, somos nosotros los encar-
gados de hacer que, efectivamente, los registros de segmento apunten a los seg-
mentos especificados.
En caso de que el ensamblador encuentre un acceso a una etiqueta definida en
un segmento no mencionado en el ASSUME, se quejará con un error del tipo
'unreachable data' ('datos inaccesibles').
Si un segmento no va a estar apuntando a ninguno de los segmentos definidos
en el listado (por ejemplo, si apunta a la memoria de vídeo), se puede añadir
un ASSUME con nombre de segmento 'NOTHING', de manera que el ensamblador no
asuma que el registro de segmento en cuestión permite acceder a ninguna varia-
ble conocida. Al principio del proceso de ensamblado, el ensamblador está como
si hubiera leído una línea así:
ASSUME CS:NOTHING, DS:NOTHING, ES:NOTHING, SS:NOTHING
Es importante comprender el funcionamiento de la directiva ASSUME, ya que
puede causar confusiones en algunos casos.
Dentro de un segmento de código, las instrucciones incluidas pueden estar
agrupadas en bloques lógicos, llamados 'procedimientos'. Estos funcionan a modo
de las funciones de C o los procedimientos y funciones de Pascal. Aunque las
últimas versiones de los ensambladores permiten dar listas de argumentos, va-
riables locales, etc... (generando el código oportuno para manejarlos), no en-
traremos en ello. Para nosotros no será más que una manera de agrupar el código
en bloques lógicos, y de identificar estos bloques mediante un nombre. El nom-
bre de un procedimiento entra en la tabla de símbolos, y se le asocia la di-
rección de la primera instrucción después de la directiva 'PROC'.
En el programa HELLO, tenemos un único PROC, al que llamamos 'Entrada'.
Veamos las líneas detalladamente:
* mov ax,DATOS
La etiqueta DATOS está en la tabla de símbolos, asociada al segmento de este
nombre. El ensamblador genera en el '.OBJ' una referencia, que irá después en
el '.EXE', de manera que esta instrucción cargue en el registro AX el valor de
segmento necesario para que 'ax:0' apunte al primer byte del segmento datos.
Este valor variará de una vez que se cargue el programa a otra, pero el carga-
dor del DOS hace que siempre se asigne el valor correcto a AX.
* mov ds,ax
Hace que DS, efectivamente, apunte al segmento de código del programa. Así,
podremos acceder a la cadena contenida en este segmento.
* mov dx, OFFSET Msg
Carga DX con el offset de la etiqueta Msg dentro de su segmento. Ya que Msg
está al principio del segmento, dx se cargará con el valor 0. Aunque el progra-
ma pueda cargarse en cualquier punto de la memoria, el offset de algún dato o
alguna instrucción dentro de su segmento será siempre el mismo, por lo que esta
instrucción no se incluye en la tabla de reubicación del '.EXE'.
* mov ah,9
* int 21h
Se invoca el servicio 9 de la interrupción 21h del DOS. Este servicio escri-
en la pantalla la cadena apuntada por DS:DX (que en este caso será la cadena
Msg). Se retorna al encontrar el carácter '$', que no se imprime. Podemos apre-
ciar que en el segmento DATOS aparece el símbolo '$' dentro de la propia
cadena.
* mov ax,4C00h
* int 21h
Se invoca el servicio 4Ch de la interrupción 21h. Este servicio retorna al
programa que invocó al programa actual (normalmente al COMMAND.COM), con un
nivel de error ('ERRORLEVEL') especificado en el registro AL. En este caso, se
retorna el valor 0 (que habitualmente indica ausencia de errores).
Después de cerrar el procedimiento 'Entrada' y el segmento 'CODIGO', encon-
tramos una última directiva, 'END', con el nombre del procedimiento como pará-
metro. Esta directiva puede llevar un único parámetro, opcional, que indica el
punto de entrada al programa. En caso de linkar varios '.OBJ' para generar un
ejecutable, sólo uno de ellos puede especificar punto de entrada (pero necesa-
riamente alguno debe especificarlo), por lo que el resto de los módulos termi-
nan su listado con un simple 'END'. En este caso, ya que 'Entrada' se asocia a
la primera instrucción del procedimiento, el punto de entrada es la instrucción
'mov ax,DATOS'.
Antes de pasar a ver las directivas simplificadas de segmento, veamos un
punto importante de los ensambladores. Los ensambladores comerciales llevan
incorporado un evaluador de expresiones, de manera que en cualquier lugar donde
el ensamblador espere una constante puede aparecer una expresión que pueda
evaluarse en tiempo de ensamblado. Estas expresiones pueden llevar operadores;
podremos encontrar una lista de todos en el manual del ensamblador o en la ayu-
da online. Los más importantes son los siguientes:
+,-,*,/,MOD Aritméticos
OR, AND, XOR, NOT Lógicos
SEG, OFFSET Aplicados a una etiqueta, dan el valor de segmento donde
reside (se incluye una entrada en la tabla de relocaliza-
ción) y el offset dentro de su segmento
SHL, SHR Sintáxis: X SHL Y. Devuelve el valor X desplazado a la
izquierda (derecha) Y bits.
() Se utilizan para saltarse la precedencia de operadores
Líneas válidas utilizando estos operadores son las siguientes (suponiendo
que todas las etiquetas hayan sido definidas previamente):
Msg DW 55h SHR 2
Variable DB 15 + OFFSET Msg
mov ax,4+3*OFFSET Msg
mov dx,2 + NOT 01010101b
add ax,es:[bx+di+1+OFFSET Msg]
sub si,(2Ah+13d)*2
Son todas válidas porque las expresiones que aparecen puede evaluarlas el
propio ensamblador para obtener valores numéricos. Las siguientes instruccio-
nes, en cambio, aunque perfectamente inteligibles, son ilegales y el ensambla-
dor se quejará si las incluímos:
mov ax,2+bx
add dx,bx*ax
mov cx,NOT dx
Veamos ahora una versión del programa anterior utilizando las directivas de
segmento simplificadas. Veamos cómo se simplifica mucho:
=================8<============================8<===========================
.MODEL SMALL ; Un segmento de código, otro con los datos y la pila
.STACK 200h ; 200h (512d) bytes de pila
.DATA ; Abre el segmento de datos
Msg DB 'Hello, world!$'
.CODE ; Abre el segmento de código, cierra el de datos
Entrada PROC ; Abre el procedimiento 'Entrada'
mov ax,@data
mov ds,ax
mov dx,OFFSET Msg
mov ah,9
int 21h ; Servicio 9: imprimir cadena
mov ax,4C00h
int 21h ; Servicio 4Ch: retorno al DOS
Entrada ENDP ; Cierra el procedimiento 'Entrada'
END Entrada ; Fin del programa (punto de entrada 'Entrada'), cierra
; el segmento de código
=================8<============================8<===========================
Las nuevas directivas utilizadas son las que comienzan con un punto. La
primera, '.MODEL', especifica el modelo de memoria que se va a utilizar. En
función de el modelo de memoria se seleccionan los nombres de los segmentos, de
manera que se pueda linkar el módulo con otro módulo escrito en un lenguaje de
alto nivel usando el mismo modelo de memoria. La directiva '.STACK' genera un
segmento de pila, de 1024 bytes en caso de que no se especifique el tamaño. La
directiva '.DATA' abre el segmento de datos, cerrando el segmento anterior en
caso de que haya alguno abierto. La directiva '.CODE' abre el segmento de códi-
go, cerrando (en este caso) el segmento de datos. Por fin, la directiva 'END'
es ampliada para cerrar el segmento abierto cuando aparece, en este caso el
segmento de código.
Ya que no sabemos el nombre que da el ensamblador a los distintos segmentos,
existen algunos símbolos que se definen automáticamente al encontrar la direc-
tiva '.MODEL'. Estos símbolos representan el nombre de los distintos segmentos
generados, y son dos: @code y @data. Por eso, en la primera instrucción carga-
mos DS con @data, para poder acceder a 'Msg', que está en el segmento dado por
'.DATA'.
El propio ensamblador genera el 'ASSUME...' al encontrar la directiva
'.CODE'. Como vemos, no es necesario preocuparse por casi nada al usar las
directivas simplificadas de segmento. De hecho, ya no es habitual encontrar
programas escritos con las directivas antiguas. Estas directivas se añadieron
para facilitar el desarrollo de programas con módulos en distintos lenguajes,
ya que la tarea se complica mucho usando las directivas clásicas.
En el siguiente capítulo veremos el último concepto que nos queda por ver
de microprocesadores: los flags. Veremos algunas instrucciones nuevas y alguna
interrupción nueva, viéndolo todo con un pequeño programa de ejemplo. De aquí
en adelante, iremos cubriendo distintos temas progresivamente, de manera que
se vean ejemplos de todos los usos habituales del lenguaje ensamblador. De
esta manera, aunque el curso no será muy útil como referencia de ASM, quien
lo siga habrá visto la mayoría de los aspectos del ASM y podrá abordar cual-
quier proyecto en este lenguaje, que es cuando realmente adquirirá verdadero
dominio del lenguaje. Os recomiendo que os hagáis al menos con dos manuales o
guías de referencia: una con las instrucciones del 80x86, y otra con las
interrupciones de la BIOS y el MSDOS. Sobre las instrucciones no me atrevo a
recomendar ninguna, mientras que sobre las interrupciones tenéis por ahí unos
documentos, escritos por Ralph Brown, denominados INTERxxA.ARJ, INTERxxB.ARJ e
INTERxxC.ARJ (xx es la versión, la última es la 33, creo) con la más completa
referencia de interrupciones que existe (son muchos megas de texto!). Son de
dominio público, por lo que las encontraréis en bastantes BBS. Ahí tendréis
todo lo que queráis saber sobre los usos de las diferentes interrupciones.
Salut :-)
Jon